package com.example.service.impl;

import com.example.global.GlobalWord;
import com.example.pojo.*;
import com.example.req.DetailReq;
import com.example.req.WorkOrderTaskReq;
import com.example.service.*;
import com.example.utils.Utils;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.bpmn.model.UserTask;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.stream.Collectors;

@Service
public class DetailProcessServiceImpl implements DetailProcessService {
    @Autowired
    RepositoryService repositoryService;
    @Autowired
    RuntimeService runtimeService;
    @Autowired
    TaskService taskService;
    @Autowired
    HistoryService historyService;

    @Autowired
    IDetailService detailService;
    @Autowired
    IWorkOrderService workOrderService;
    @Autowired
    ISysUserService sysUserService;
    @Autowired
    ISysOfficeService officeService;
    @Autowired
    ITransferService transferService;

    /**
     * 开启流程
     * @param detailReq
     * @return
     */
    @Override
    @Transactional
    public boolean startDetailProcess(DetailReq detailReq) {
        //1、插入新的工单
        WorkOrder workOrder = new WorkOrder();
        workOrder.setCreateUserId(detailReq.getCreateUserId());
        workOrder.setCode(System.currentTimeMillis() + "" + new Random().nextInt(100));
        workOrder.setStatus(0);
        boolean save1 = workOrderService.save(workOrder);
        System.out.println("回填的工单id" + workOrder.getId());

        //2、插入关联操作记录
        Transfer transfer = new Transfer();
        transfer.setOprateUserId(workOrder.getCreateUserId());
        transfer.setWorkOrderId(workOrder.getId());
        SysOffice office = officeService.queryByUserId(workOrder.getCreateUserId());
        transfer.setContent("产废方【" + office.getName() + "】发起工单");
        boolean save2 = transferService.save(transfer);

        //3、插入处理的危废品详情记录
        detailReq.setWorkOrderId(workOrder.getId());
        boolean save = detailService.save(detailReq);
        System.out.println("回填的工单详情id" + detailReq.getId());

        //4、合成流程定义的唯一标识businessKey
        String key = GlobalWord.workOrderKey;
        String businessKey = key + "." + detailReq.getId();

        //5、获取流程关联的代办人集合
        Map<String, Object> map = new HashMap<>();
        //输入产废方ID
        map.put("wasteUserId", detailReq.getCreateUserId());
        //获取所有处置方和运输方ID集合，并将ID集合转变为字符串
        List<SysUser> shippingParty = sysUserService.queryByQualification("2");
        List<SysUser> disposingParty = sysUserService.queryByQualification("3");
        List<Long> shippingPartyUserIds = shippingParty.stream().map(e -> e.getId()).collect(Collectors.toList());
        List<Long> disposingPartyUserIds = disposingParty.stream().map(e -> e.getId()).collect(Collectors.toList());
        String shippingPartyUserIdsStr = Utils.listToString(shippingPartyUserIds);
        String disposingPartyUserIdsStr = Utils.listToString(disposingPartyUserIds);

        map.put(GlobalWord.shippingParty, shippingPartyUserIdsStr);
        map.put(GlobalWord.disposingParty, disposingPartyUserIdsStr);
        //开启流程
        runtimeService.startProcessInstanceByKey(key, businessKey, map);

        return save && save1 && save2;
    }

    /**
     * 根据用户ID获取任务集合
     * @param id
     * @return
     */
    @Override
    public List<Task> queryDetailTaskByUserId(Long id) {
        //根据用户组查询(该操作需要SpringSecurity支持，不然报异常)
        List<Task> list = taskService.createTaskQuery()
                .processDefinitionKey(GlobalWord.workOrderKey)
                .taskCandidateUser(id.toString())
                .list();
        //根据Assignee查询
        List<Task> list1 = taskService.createTaskQuery()
                .processDefinitionKey(GlobalWord.workOrderKey)
                .taskAssignee(id.toString())
                .list();
        //将两个任务集合合并返回
        list.addAll(list1);
        return list;
    }

    /**
     * 根据TaskId获取businessKey
     * @param taskId
     * @return
     */
    @Override
    public String getBusinessKeyByTaskId(String taskId) {
        //1、获取任务对象
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        //2、根据流程实例ID获取流程实例，进一步获取businessKey返回
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
        return processInstance.getBusinessKey();
    }

    /**
     * 根据TaskId获取关联批注
     * @param taskId
     * @return
     */
    @Override
    public List<Comment> queryAllCommentByTaskId(String taskId) {
        //1、获取任务对象
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        //2、获取批注集合返回
        List<Comment> comments = taskService.getProcessInstanceComments(task.getProcessInstanceId());
        return comments;
    }

    /**
     * 根据taskID获取当前节点的出口连线名称集合
     * @param taskId
     * @return
     */
    @Override
    public List<String> queryButtonListByTaskId(String taskId) {
        // 获取当前任务
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        //获取当前模型
        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
        // 获取当前节点
        FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey());
        UserTask userTask = (UserTask) flowElement;
        //获取节点出口连线集合
        List<SequenceFlow> outgoingFlows = userTask.getOutgoingFlows();
        //获取连线集合的名称集合
        List<String> collect = outgoingFlows.stream()
                .map(e -> e.getName() == null ? "默认提交" : e.getName())
                .collect(Collectors.toList());

        return collect;
    }

    /**
     * 工单流程推进
     * @param info
     * @return
     */
    @Override
    @Transactional
    public boolean complete(WorkOrderTaskReq info) {
        //推进条件的Map集合
        Map<String, Object> map = new HashMap<>();

        //1、获取任务对象，并得到businessKey
        Task task = taskService.createTaskQuery()
                .taskId(info.getTaskId())
                .singleResult();
        String businessKey = getBusinessKeyByTaskId(task.getId());

        //2、若当前节点是对对人开放操作，则推进前先领取任务，避免多人同时操作发生冲突
        if (task.getAssignee() == null) {
            //领取任务，相当于设置assignee，领取任务后，他人不能再领取
            taskService.claim(info.getTaskId(), info.getUserId().toString());

            //若当前结点是处置方确认节点，则添加处置方ID，并修改相应工单信息
            if (task.getName().equals("处置方确认")){
                //添加处置人验收节点的办理人
                map.put("disposingUserId",info.getUserId());

                //更新工单接收人信息
                Detail detail = detailService.getById(businessKey.split("\\.")[1]);
                WorkOrder workOrder = workOrderService.getById(detail.getWorkOrderId());
                workOrder.setRecipientUserId(info.getUserId());
                workOrderService.updateById(workOrder);
                //若当前结点为运输方确认节点，更新关联的表信息
            }else if (task.getName().equals("运输方确认")){
                //设置detail表的车牌号信息
                Detail detail = detailService.getById(businessKey.split("\\.")[1]);
                detail.setPlateNumber(info.getPlateNumber());
                detailService.updateById(detail);

                //设置workOrder表的状态和运输人信息
                WorkOrder workOrder = workOrderService.getById(detail.getWorkOrderId());
                workOrder.setStatus(1);
                workOrder.setTransportUserId(info.getUserId());
                workOrderService.updateById(workOrder);

                //transfer表添加对应的操作记录
                Transfer transfer = new Transfer();
                transfer.setOprateUserId(info.getUserId());
                transfer.setWorkOrderId(detail.getWorkOrderId());
                SysOffice office = officeService.queryByUserId(info.getUserId());
                transfer.setContent("运输方【" + office.getName() + "】接受移交|车牌号：" + detail.getPlateNumber());
                transferService.save(transfer);
            }
        }

        //若当前结点为处置方验收结点，添加相应的操作记录
        if (task.getName().equals("处置方验收")){
            Detail detail = detailService.getById(businessKey.split("\\.")[1]);
            Transfer transfer = new Transfer();
            transfer.setOprateUserId(info.getUserId());
            transfer.setWorkOrderId(detail.getWorkOrderId());
            SysOffice office = officeService.queryByUserId(info.getUserId());
            transfer.setContent("处置方【" + office.getName() + "】接受移交");
            transferService.save(transfer);
        }

        //设置批注人以及批注信息
        Authentication.setAuthenticatedUserId(info.getUserId().toString());
        taskService.addComment(task.getId(), task.getProcessInstanceId(), info.getComment());

        //添加流程推进条件并推进流程
        map.put("msg", info.getButtonValue());
        taskService.complete(task.getId(), map);

        //获取流程实例
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceBusinessKey(businessKey)
                .singleResult();

        //判断流程是否结束，若结束则修改对应表信息
        if (processInstance == null){
            //更新detail表状态
            Detail detail = detailService.getById(businessKey.split("\\.")[1]);
            WorkOrder workOrder = workOrderService.getById(detail.getWorkOrderId());
            workOrder.setStatus(2);
            workOrderService.updateById(workOrder);

            //添加操作记录
            Transfer transfer = new Transfer();
            transfer.setOprateUserId(info.getUserId());
            transfer.setWorkOrderId(detail.getWorkOrderId());
            transfer.setContent("处置流程已结束，感谢使用危废智能监管平台！");
            transferService.save(transfer);
        }
        return true;
    }
}
